#include <stdlib.h>
#include <sys/epoll.h>
#include <errno.h>
#include <string.h>
#include "pd_event.h"
#include "pd_log.h"
#include "pd_socket.h"

struct PdSocketEvent
{
    int epoll_fd;
};

static PdSocketEvent *alloc_();

static void free_(PdSocketEvent *ev);

////////////////////////////////////////////////////////////////////////////////////////////////////

PdSocketEvent *alloc_()
{
    PdSocketEvent *ret = (PdSocketEvent*)malloc(sizeof(PdSocketEvent));
    if (NULL != ret)
    {
        ret->epoll_fd = -1;
    }
    return ret;
}

void free_(PdSocketEvent *ev)
{
    if (NULL != ev)
    {
        free(ev);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////

PdSocketEvent *pd_event_init()
{
    PdSocketEvent *ret = NULL;
    if (NULL == (ret = alloc_()))
    {
        PD_LOG(WARN, "alloc PdSocketEvent fail");
    }
    else if (-1 == (ret->epoll_fd = epoll_create(MAX_SOCKET_EVENTS)))
    {
        PD_LOG(WARN, "epoll_create fail, errno=%u", errno);
        free(ret);
        ret = NULL;
    }
    else
    {
        PD_LOG(INFO, "epoll_create succ, epoll_fd=%d", ret->epoll_fd);
    }
    return ret;
}

void pd_event_destroy(PdSocketEvent *ev)
{
    if (NULL == ev)
    {
        PD_LOG(WARN, "ev null pointer");
    }
    else
    {
        if (-1 != ev->epoll_fd)
        {
            close(ev->epoll_fd);
            ev->epoll_fd = -1;
        }
        free_(ev);
    }
}

int pd_event_add_event(PdSocketEvent *ev, PdIOComponent *ioc, int enable_read, int enable_write)
{
    int ret = 0;
    if (NULL == ev || NULL == ioc || NULL == ioc->sock)
    {
        PD_LOG(WARN, "invalid param, ev=%p ioc=%p sock=%p", ev, ioc, ioc->sock);
        ret = -1;
    }
    else
    {
        struct epoll_event epoll_ev;
        memset(&epoll_ev, 0, sizeof(epoll_ev));
        epoll_ev.data.ptr = ioc;
        epoll_ev.events = 0;
        if (enable_write)
        {
            epoll_ev.events |= EPOLLOUT;
        }
        if (enable_read)
        {
            epoll_ev.events |= EPOLLIN;
        }
        if (0 != epoll_ctl(ev->epoll_fd, EPOLL_CTL_ADD, pd_socket_get_fd(ioc->sock), &epoll_ev))
        {
            PD_LOG(WARN, "epoll_ctl add fail, errno=%u epoll_fd=%d sock_fd=%d ioc=%p",
                    errno, ev->epoll_fd, pd_socket_get_fd(ioc->sock), ioc);
            ret = -1;
        }
        else
        {
            PD_LOG(INFO, "epoll_ctl add succ, epoll_fd=%d sock_fd=%d ioc=%p",
                    ev->epoll_fd, pd_socket_get_fd(ioc->sock), ioc);
        }
    }
    return ret;
}

int pd_event_set_event(PdSocketEvent *ev, PdIOComponent *ioc, int enable_read, int enable_write)
{
    int ret = 0;
    if (NULL == ev || NULL == ioc || NULL == ioc->sock)
    {
        PD_LOG(WARN, "invalid param, ev=%p ioc=%p sock=%p", ev, ioc, ioc->sock);
        ret = -1;
    }
    else
    {
        struct epoll_event epoll_ev;
        memset(&epoll_ev, 0, sizeof(epoll_ev));
        epoll_ev.data.ptr = ioc;
        epoll_ev.events = 0;
        if (enable_write)
        {
            epoll_ev.events |= EPOLLOUT;
        }
        if (enable_read)
        {
            epoll_ev.events |= EPOLLIN;
        }
        if (0 != epoll_ctl(ev->epoll_fd, EPOLL_CTL_MOD, pd_socket_get_fd(ioc->sock), &epoll_ev))
        {
            PD_LOG(WARN, "epoll_ctl mod fail, errno=%u epoll_fd=%d sock_fd=%d ioc=%p",
                    errno, ev->epoll_fd, pd_socket_get_fd(ioc->sock), ioc);
            ret = -1;
        }
        else
        {
            PD_LOG(INFO, "epoll_ctl mod succ, epoll_fd=%d sock_fd=%d ioc=%p",
                    ev->epoll_fd, pd_socket_get_fd(ioc->sock), ioc);
        }
    }
    return ret;
}

int pd_event_remove_event(PdSocketEvent *ev, PdIOComponent *ioc)
{
    int ret = 0;
    if (NULL == ev || NULL == ioc || NULL == ioc->sock)
    {
        PD_LOG(WARN, "invalid param, ev=%p ioc=%p sock=%p", ev, ioc, ioc->sock);
        ret = -1;
    }
    else
    {
        struct epoll_event epoll_ev;
        memset(&epoll_ev, 0, sizeof(epoll_ev));
        epoll_ev.data.ptr = ioc;
        epoll_ev.events = 0;
        if (0 != epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, pd_socket_get_fd(ioc->sock), &epoll_ev))
        {
            PD_LOG(WARN, "epoll_ctl del fail, errno=%u epoll_fd=%d sock_fd=%d ioc=%p",
                    errno, ev->epoll_fd, pd_socket_get_fd(ioc->sock), ioc);
            ret = -1;
        }
        else
        {
            PD_LOG(INFO, "epoll_ctl del succ, epoll_fd=%d sock_fd=%d ioc=%p",
                    ev->epoll_fd, pd_socket_get_fd(ioc->sock), ioc);
        }
    }
    return ret;
}

int pd_event_get_events(PdSocketEvent *ev, int timeout_us, PdIOEvent *events, int events_cnt)
{
    int ret = 0;
    if (NULL == ev || NULL == events || 0 >= events_cnt)
    {
        PD_LOG(WARN, "invalid param, ev=%p events=%p events_cnt=%d", ev, events, events_cnt);
        ret = -1;
    }
    else
    {
        struct epoll_event epoll_events[MAX_SOCKET_EVENTS];
        if (events_cnt > MAX_SOCKET_EVENTS)
        {
            events_cnt = MAX_SOCKET_EVENTS;
        }

        ret = epoll_wait(ev->epoll_fd, epoll_events, events_cnt, timeout_us / 1000);

        if (0 < ret)
        {
            memset(events, 0, sizeof(PdIOEvent) * (uint32_t)ret);
        }

        int i = 0;
        for (; i < ret; i++)
        {
            events[i].ioc = epoll_events[i].data.ptr;
            if (epoll_events[i].events & (EPOLLERR | EPOLLHUP))
            {
                events[i].error = 1;
            }
            if ((epoll_events[i].events & EPOLLIN) != 0)
            {
                events[i].readable = 1;
            }
            if ((epoll_events[i].events & EPOLLOUT) != 0)
            {
                events[i].writeable = 1;
            }
        }
    }
    return ret;
}

